#include "baidu_ai_asr.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <openssl/bio.h>    // OpenSSL BIO库头文件
#include <openssl/evp.h>    // OpenSSL EVP库头文件
#include <openssl/buffer.h> // OpenSSL BUF库头文件

using namespace std;

// 构造函数实现
BaiduAiAsr::BaiduAiAsr(const string &apiKey, const string &secretKey)
    : apiKey_(apiKey), secretKey_(secretKey), expiresIn_(0)
{
    // 初始化时立即获取访问令牌
    requestAccessToken();
}

BaiduAiAsr::~BaiduAiAsr()
{
    // 析构函数，目前没有需要特殊清理的资源
}

// 请求访问令牌实现
void BaiduAiAsr::requestAccessToken()
{
    try
    {
        // 初始化CURL
        CURL *curl = curl_easy_init();
        if (!curl)
        {
            throw std::runtime_error("CURL初始化失败");
        }

        // 构建获取token的URL，包含API Key和Secret Key
        std::string url = "https://aip.baidubce.com/oauth/2.0/token?"
                          "grant_type=client_credentials&"
                          "client_id=" +
                          apiKey_ +
                          "&client_secret=" + secretKey_;

        // 设置HTTP请求头
        std::string response;
        struct curl_slist *headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, "Accept: application/json");

        // 配置CURL选项
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

        // 设置超时时间
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);        // 总超时30秒
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); // 连接超时10秒

        // 执行HTTP请求
        CURLcode res = curl_easy_perform(curl);

        // 清理资源
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);

        // 检查请求是否成功
        if (res != CURLE_OK)
        {
            throw std::runtime_error("令牌请求失败: " + string(curl_easy_strerror(res)));
        }

        // 解析返回的JSON数据
        Json::Value root;
        Json::CharReaderBuilder readerBuilder;
        std::string errors;
        istringstream responseStream(response);

        if (!Json::parseFromStream(readerBuilder, responseStream, &root, &errors))
        {
            throw std::runtime_error("无效的JSON响应: " + errors);
        }

        // 检查是否有错误信息
        if (root.isMember("error"))
        {
            throw std::runtime_error("认证失败: " + root["error_description"].asString());
        }

        // 检查必要字段是否存在
        if (!root.isMember("access_token"))
        {
            throw std::runtime_error("认证失败: 响应中缺少access_token");
        }
        if (!root.isMember("expires_in"))
        {
            throw std::runtime_error("认证失败: 响应中缺少expires_in");
        }

        // 存储获取到的令牌信息
        accessToken_ = root["access_token"].asString();
        expiresIn_ = root["expires_in"].asUInt();
        tokenObtainedTime_ = chrono::system_clock::now();

        // 输出调试信息
        cout << "Access Token: " << accessToken_ << endl;
        cout << "Expires In: " << expiresIn_ << " seconds" << endl;
    }
    catch (const exception &e)
    {
        cerr << "BaiduAiAsr初始化失败: " << e.what() << endl;
        throw;
    }
}

// libcurl数据接收回调函数
size_t BaiduAiAsr::onWriteData(void *buffer, size_t size, size_t nmemb, void *userp)
{
    // 将接收到的数据追加到字符串中
    string *str = static_cast<string *>(userp);
    str->append(static_cast<char *>(buffer), size * nmemb);
    return size * nmemb;
}

// 读取音频文件内容
string BaiduAiAsr::readAudioFile(const string &filename) const
{
    // 以二进制模式打开文件
    ifstream file(filename, ios::binary);
    if (!file.is_open())
    {
        throw runtime_error("无法打开音频文件: " + filename);
    }

    // 读取文件全部内容
    string content((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());
    file.close();
    return content;
}

// 检查令牌是否过期
bool BaiduAiAsr::isTokenExpired() const
{
    // 如果令牌为空，视为已过期
    if (accessToken_.empty())
        return true;

    // 计算当前时间与令牌获取时间的差值（秒）
    auto now = chrono::system_clock::now();
    auto elapsed = chrono::duration_cast<chrono::seconds>(now - tokenObtainedTime_).count();

    // 提前60秒认为令牌过期，避免在临界点使用过期令牌
    return elapsed >= (expiresIn_ - 60);
}

// 刷新访问令牌
void BaiduAiAsr::refreshToken()
{
    // 如果令牌已过期，重新请求
    if (isTokenExpired())
    {
        requestAccessToken();
    }
}

// 语音识别主函数
string BaiduAiAsr::speechRecognition(const string &audioFile, const string &format,
                                     int rate, int channel)
{
    try
    {
        // 检查并刷新令牌
        refreshToken();

        // 读取音频文件内容
        string audioData = readAudioFile(audioFile);

        if (audioData.empty())
        {
            throw runtime_error("音频文件为空或读取失败: " + audioFile);
        }

        // 使用OpenSSL进行Base64编码
        BIO *bio, *b64;
        BUF_MEM *bufferPtr;

        // 创建Base64过滤器BIO
        b64 = BIO_new(BIO_f_base64());
        // 创建内存BIO
        bio = BIO_new(BIO_s_mem());
        // 将Base64过滤器压入BIO栈
        bio = BIO_push(b64, bio);

        // 设置不添加换行符
        BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);

        // 写入音频数据进行编码
        BIO_write(bio, audioData.data(), static_cast<int>(audioData.size()));
        BIO_flush(bio);
        // 获取编码后的数据
        BIO_get_mem_ptr(bio, &bufferPtr);

        // 将编码后的数据存入字符串
        string encodedAudio(bufferPtr->data, bufferPtr->length);

        // 释放BIO资源
        BIO_free_all(bio);

        // 构建JSON请求体
        Json::Value requestJson;
        requestJson["format"] = format;                           // 音频格式
        requestJson["rate"] = rate;                               // 采样率
        requestJson["channel"] = channel;                         // 声道数
        requestJson["token"] = accessToken_;                      // 访问令牌
        requestJson["cuid"] = "5K9MMh2jUkxLjB3i5KydfFbxiTDBPayJ"; // 设备唯一标识
        requestJson["speech"] = encodedAudio;                     // Base64编码的音频数据
        requestJson["len"] = static_cast<int>(audioData.size());  // 音频数据长度

        // 将JSON对象转为字符串
        Json::StreamWriterBuilder writer;
        string requestBody = Json::writeString(writer, requestJson);

        // 初始化CURL进行语音识别请求
        CURL *curl = curl_easy_init();
        if (!curl)
        {
            throw runtime_error("CURL初始化失败");
        }

        string response;
        struct curl_slist *headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type: application/json");

        // 配置CURL选项
        curl_easy_setopt(curl, CURLOPT_URL, "https://vop.baidubce.com/server_api");
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestBody.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);

        // 执行HTTP请求
        CURLcode res = curl_easy_perform(curl);

        // 清理资源
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);

        // 检查请求是否成功
        if (res != CURLE_OK)
        {
            throw runtime_error("语音识别请求失败: " + string(curl_easy_strerror(res)));
        }

        // 解析返回的JSON数据
        Json::Value root;
        Json::CharReaderBuilder readerBuilder;
        string errors;
        istringstream responseStream(response);

        if (!Json::parseFromStream(readerBuilder, responseStream, &root, &errors))
        {
            throw runtime_error("无效的JSON响应: " + errors);
        }

        // 检查识别结果是否有错误
        if (root.isMember("err_no") && root["err_no"].asInt() != 0)
        {
            throw runtime_error("识别错误: " + root["err_msg"].asString());
        }

        // 返回识别结果
        if (root.isMember("result") && root["result"].isArray())
        {
            return root["result"][0].asString();
        }
    }
    catch (const exception &e)
    {
        cerr << "语音识别失败: " << e.what() << endl;
    }
    return "无法识别的结果";
}

std::string BaiduAiAsr::speechRecognition(const std::vector<uint8_t> &audioData)
{
    try
    {
        // 1. 检查并刷新访问令牌
        refreshToken();

        // 2. 检查音频数据是否为空
        if (audioData.empty())
        {
            throw std::runtime_error("音频数据为空");
        }

        // 3. Base64编码音频数据（使用OpenSSL）
        BIO *bio, *b64;
        BUF_MEM *bufferPtr;

        b64 = BIO_new(BIO_f_base64());
        bio = BIO_new(BIO_s_mem());
        bio = BIO_push(b64, bio);

        BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
        BIO_write(bio, audioData.data(), static_cast<int>(audioData.size()));
        BIO_flush(bio);
        BIO_get_mem_ptr(bio, &bufferPtr);

        std::string encodedAudio(bufferPtr->data, bufferPtr->length);
        BIO_free_all(bio);

        // 4. 构建JSON请求
        Json::Value requestJson;
        requestJson["format"] = "pcm"; // 默认音频格式
        requestJson["rate"] = 16000;   // 默认采样率
        requestJson["channel"] = 1;    // 默认单声道
        requestJson["token"] = accessToken_;
        requestJson["cuid"] = "5K9MMh2jUkxLjB3i5KydfFbxiTDBPayJ"; // 设备唯一标识
        requestJson["speech"] = encodedAudio;
        requestJson["len"] = static_cast<int>(audioData.size());

        Json::StreamWriterBuilder writer;
        std::string requestBody = Json::writeString(writer, requestJson);

        // 5. 发送HTTP请求到百度语音识别API
        CURL *curl = curl_easy_init();
        if (!curl)
        {
            throw std::runtime_error("CURL初始化失败");
        }

        std::string response;
        struct curl_slist *headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type: application/json");

        curl_easy_setopt(curl, CURLOPT_URL, "https://vop.baidubce.com/server_api");
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestBody.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);

        CURLcode res = curl_easy_perform(curl);
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);

        if (res != CURLE_OK)
        {
            throw std::runtime_error("语音识别请求失败: " + std::string(curl_easy_strerror(res)));
        }

        // 6. 解析API响应
        Json::Value root;
        Json::CharReaderBuilder readerBuilder;
        std::string errors;
        std::istringstream responseStream(response);

        if (!Json::parseFromStream(readerBuilder, responseStream, &root, &errors))
        {
            throw std::runtime_error("无效的JSON响应: " + errors);
        }

        // 7. 检查识别错误
        if (root.isMember("err_no") && root["err_no"].asInt() != 0)
        {
            throw std::runtime_error("识别错误: " + root["err_msg"].asString());
        }

        // 8. 返回识别结果
        if (root.isMember("result") && root["result"].isArray() && !root["result"].empty())
        {
            return root["result"][0].asString();
        }

        return nullptr;
    }
    catch (const std::exception &e)
    {
        std::cerr << "语音识别失败: " << e.what() << std::endl;
        return "语音识别失败: " + std::string(e.what());
    }
}

// 获取当前访问令牌
string BaiduAiAsr::getAccessToken() const
{
    return accessToken_;
}
